class Position {
  type;
  index;
  // 设置当前操作覆盖的区域
  activated = new Set();
  $target;
  $item;
  playground;
  size;
  start;
  end;
  move;

  constructor(type) {
    this.type = type;
  }

  setGrid = (grid) => {
    __grid = grid;
  }

  setStart(event) {
    const $item = $(event.target).closest('.sullivan-grid-item');
    const activated = $item.attr('data-activated');
    // 下标
    this.index = $item.index();
    // 目标元素
    this.$item = $item;
    // 目标元素主体
    this.$target = $item.find('.sullivan-grid-item-content');
    // 开始位置
    this.start = new Coordinate(event.pageX, event.pageY);
    // 移动距离
    this.move = new Coordinate(0, 0);
    // 大小
    this.size = new Size(this.$target);
    // 背景板
    this.playground = new Size($(event.target).closest('.sullivan-grid'));

    this.activated.add(this.index)
    if (typeof activated === 'number') {
      this.activated.add(activated)
    } else {
      activated?.split(',')?.forEach(act => this.activated.add(+act))
    }

    // console.log('start: => ', event);
  }

  setMove(event) {
    const { left, right, top, bottom } = this.playground
    /**
     * 第一层限制，必须在九宫格范围内。
     */
    if (event.pageX >= left && event.pageX <= right && event.pageY >= top && event.pageY <= bottom) {
      const { left, right, top, bottom } = this.size
      // console.log(this.$target.height(), this.size.initH, this.$target.height() >= this.size.initH)
      /**
       * 第二层限制，必须大于子区域大小。
       */
      if (this.$target.height() >= this.size.initH || this.$target.width() >= this.size.initW) {
        this.move.update(event.pageX - this.start.x, event.pageY - this.start.y);
        this.updateView()
      }
    }
    // console.log('move: => ', this.$target.height(), this.size.initH)
  }

  setEnd(event, cb = () => {}) {
    this.end = new Coordinate(event.pageX, event.pageY);
    this.setMove(event);
    this.updateView();
    this.updateLayout(cb);
    this.size.update(this.$target);
    console.log('end: => ', this)
  }

  /**
   * 拖动时，更新视图。
   */
  updateView () {
    const { width, height } = this.size
    const { x: left, y: top } = this.move
    switch (this.type) {
      case 'top': {
        return this.$target.css({ height: height - top, top })
      }
      case 'right': {
        return  this.$target.css({ width: width + left })
      }
      case 'bottom': {
        return this.$target.css({ height: height + top })
      }
      case 'left': {
        return this.$target.css({ width: width - left, left })
      }
    }
  }

  /**
   * 吞并周边，放大
   *
   * @param { string } area 区域 key 值
   * @param { number } moveDistance 移动距离
   * @param { number } initSize 基础值，大于基础之，则会递归添加下一个
   * @param { number[] } activated 下标集合
   * @param { number } gap 下一个间隔，距离下一个下标的间隔
   * @param { string[] } hide 即将被隐藏的区域集合
   */
  updateGridAreas (area, moveDistance, initSize, activated, gap, hide = new Set()) {
    const __activated = activated.map(act => {
      const idx = act + gap
      if (!activated.includes(idx)) {
        hide.add(__grid.areasCopy[idx])
        __grid.update(idx, area)
      }
      return idx
    })

    console.log('updateGridAreas: => ', __activated)
    // 移动距离 > 基础值，递归添加下一个
    if (moveDistance > initSize) {
      this.updateGridAreas(area, moveDistance - initSize, initSize, __activated, gap, hide)
    }
    return hide
  }

  /**
   * 抛弃周边，缩小
   *
   * @param { number } moveDistance 移动距离
   * @param { number } initSize 基础值
   * @param { number[] } activated 下标集合
   * @param { number } gap 下一个间隔，距离下一个下标的间隔
   * @param { string[] } show 即将被显示的区域集合
   */
  removeGridAreas (moveDistance, initSize, activated, gap, show = new Set()) {
    const dataIndex = this.$item.attr('data-index')
    const acts = []
    let __activated
    let prev = activated.shift();
    acts.push(prev)
    __activated = activated.filter(item => {
      if (item === prev + gap) {
        acts.push(item)
        prev = item
      } else {
        return item;
      }
    })

    // console.log('removeGridAreas: => ', acts, __activated)
    // 暂时不允许自身初始位置。
    if (acts.includes(dataIndex - 1)) return show;
    acts.forEach(index => {
      const area = __grid.areas[index]
      show.add(area)
      __grid.update(index, area)
    })

    if (moveDistance > initSize) {
      // 移动距离 > 基础值，递归删除下一个
      this.removeGridAreas(moveDistance - initSize, initSize, __activated, gap, show)
    }
    return show
  }

  /**
   * 隐藏周边，配合 updateGridAreas 使用
   * 将 grid.areasCopy 中不存在的隐藏掉
   */
  hideGridItem (hideAreas) {
    hideAreas.forEach(area => {
      if (!__grid.areasCopy.includes(area)) {
        __grid.$grid.find(`.${area}`).hide()
      }
    })
  }

  /**
   * 显示周边，配合 removeGridAreas 使用
   * 将已隐藏，grid.areasCopy 中重新添加的，显示出来
   *
   * @param areas
   */
  showGridItem (areas) {
    areas.forEach(area => {
      __grid.$grid.find(`.${area}`).show()
    })
  }

  /**
   * 寻找到当前选中项的所有下标位置
   *
   * @param originArea
   * @returns {*[]}
   */
  findActivated (originArea) {
    console.log('findActivated: => ', originArea)
    const areas = []
    __grid.areasCopy.forEach((area, index) => {
      if (area === originArea) {
        areas.push(index)
      }
    })
    return areas;
  }

  /**
   * 更新布局
   */
  updateLayout (callback) {
    const area = this.$item.attr('data-area')
    const { initH, initW } = this.size
    const activated = this.findActivated(area)
    switch (this.type) {
      case 'top': {
        if (this.move.y < -20) {
          const hideAreas = this.updateGridAreas(area, -this.move.y, initH, activated, -__grid.row);
          callback()
          this.hideGridItem(hideAreas);
          console.log('data-activated:enlarge => ', activated, hideAreas)
        } else if (this.move.y > 20) {
          const showAreas = this.removeGridAreas(this.move.y, initH, activated, 1);
          this.showGridItem(showAreas);
          console.log('data-activated:shrink => ', activated, showAreas)
        }
        break
      }
      case 'right': {
        if (this.move.x < -20) {
          const showAreas = this.removeGridAreas(this.move.x, initW, activated.reverse(), -__grid.col);
          this.showGridItem(showAreas);
          console.log('data-activated:shrink => ', activated, showAreas)
        } else if (this.move.x > 20) {
          const hideAreas = this.updateGridAreas(area, this.move.x, initW, activated, 1);
          this.hideGridItem(hideAreas);
          console.log('data-activated:enlarge => ', activated, hideAreas)
        }
        break
      }
      case 'bottom': {
        if (this.move.y < -20) {
          const showAreas = this.removeGridAreas(this.move.y, initH, activated.reverse(), -1);
          this.showGridItem(showAreas);
          console.log('data-activated:shrink => ', activated, showAreas)
        } else if (this.move.y > 20) {
          const hideAreas = this.updateGridAreas(area, this.move.y, initH, activated, __grid.row);
          this.hideGridItem(hideAreas);
          console.log('data-activated:enlarge => ', activated, hideAreas)
        }
        break
      }
      case 'left': {
        if (this.move.x < -20) {
          const hideAreas = this.updateGridAreas(area, -this.move.x, initW, activated, -1);
          this.hideGridItem(hideAreas);
          console.log('data-activated:enlarge => ', activated, hideAreas)
        } else if (this.move.x > 20) {
          const showAreas = this.removeGridAreas(this.move.x, initW, activated, __grid.col);
          this.showGridItem(showAreas);
          console.log('data-activated:shrink => ', activated, showAreas)
        }
        break
      }
    }

    this.$target.removeAttr('style')
    this.$item.attr('data-activated', [...this.activated.values()])
  }
}